package com.ruoyi.web.service.impl;

import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.web.enums.AbnormalEnum;
import com.ruoyi.framework.web.exception.TadpoleException;
import com.ruoyi.web.constant.WalletConstant;
import com.ruoyi.web.domain.AppUser;
import com.ruoyi.web.domain.Currency;
import com.ruoyi.web.domain.UserAddress;
import com.ruoyi.web.domain.WithdrawalRecord;
import com.ruoyi.web.dto.ApplyWithdrawalDTO;
import com.ruoyi.web.dto.WithdrawalToExamineDTO;
import com.ruoyi.web.enums.WalletStatusEnums;
import com.ruoyi.web.mapper.AppUserMapper;
import com.ruoyi.web.mapper.UserAddressMapper;
import com.ruoyi.web.mapper.UserCapitalMapper;
import com.ruoyi.web.mapper.WithdrawalRecordMapper;
import com.ruoyi.web.service.ICurrencyService;
import com.ruoyi.web.service.IUserCapitalService;
import com.ruoyi.web.service.IWithdrawalRecordService;
import com.ruoyi.web.utils.IdWorker;
import com.ruoyi.web.utils.MysqlUtil;
import lombok.AllArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;

/**
 * 提现记录Service业务层处理
 *
 * @author ruoyi
 * @date 2023-01-05
 */
@Service
@AllArgsConstructor
public class WithdrawalRecordServiceImpl implements IWithdrawalRecordService {
    private final WithdrawalRecordMapper withdrawalRecordMapper;

    private final StringRedisTemplate stringRedisTemplate;
    private final ICurrencyService currencyService;
    private final AppUserMapper appUserMapper;
    private final UserAddressMapper addressMapper;
    private final MysqlUtil mysqlUtil;
    private final IUserCapitalService capitalService;
    private final UserCapitalMapper capitalMapper;
    private final IdWorker idWorker;

    public final RedisCache redis;

    /**
     * 申请提现
     *
     * @return r
     */
    @Override
    public R<String> create(ApplyWithdrawalDTO dto) {
        try {
            // 必填校验
            if (dto.getArrivalAddress() == null || dto.getArrivalAddress().isEmpty()) {
                throw new TadpoleException(AbnormalEnum.MSG.MANDATORY_PARAM_EMPTY_OR_MALFORMED.getCode(), "The pay-in address cannot be empty");
            }
            if (dto.getQuantity() == null || dto.getQuantity().compareTo(BigDecimal.ZERO) <= 0) {
                throw new TadpoleException(AbnormalEnum.MSG.MANDATORY_PARAM_EMPTY_OR_MALFORMED.getCode(), "Withdrawal quantity cannot be less than or equal to 0");
            }
            if (dto.getChain() == null || dto.getChain().isEmpty()) {
                throw new TadpoleException(AbnormalEnum.MSG.MANDATORY_PARAM_EMPTY_OR_MALFORMED.getCode(), "Public chain cannot be empty");
            }
            // 获取币种
            Currency currency = currencyService.getCurrency(dto.getChain());
            if (currency == null) {
                throw new TadpoleException(AbnormalEnum.MSG.DISCONNECTED.getCode(), "Currency does not exist, please contact the administrator");
            }
            if (dto.getQuantity().compareTo(currency.getMinimumWithdrawalAmount()) < 0) {
                throw new TadpoleException(AbnormalEnum.MSG.MANDATORY_PARAM_EMPTY_OR_MALFORMED.getCode(), "The withdrawal amount cannot be less than the minimum withdrawal amount");
            }
            Long userId = SecurityUtils.getUserId();
            AppUser appUser = appUserMapper.selectAppUserById(userId);
            // 检测是否有未完成的提现申请
            int count = withdrawalRecordMapper.selectWithdrawalRecordList(new WithdrawalRecord() {{
                setUserId(userId);
                setState(WalletStatusEnums.Status.UNDER_REVIEW.getValue());
            }}).size();
            if (count > 0) {
                throw new TadpoleException(AbnormalEnum.MSG.UNSUPPORTED_OPERATION.getCode(), "You still have an outstanding withdrawal application. Please do not resubmit the application");
            }
            // 邮箱验证码
//            String key = appUser.getEmail() + " massage = " + UserEnums.EmailType.Withdrawal_Request.getType();
//            String codeRedis = stringRedisTemplate.opsForValue().get(key);
//            if (!StrUtil.equals(dto.getVerificationCode(), codeRedis)) {
//                throw new TadpoleException(AbnormalEnum.MSG.UNKNOWN.getCode(),"Verification code error");
//            }
            // 查询用户地址
            UserAddress account = addressMapper.selectOne(new UserAddress() {{
                setUserId(appUser.getId());
                setChain(dto.getChain());
            }});
            if (account == null) {
                throw new TadpoleException(AbnormalEnum.MSG.UNKNOWN.getCode(), "The user address was not found");
            }
            mysqlUtil.startLocalTx();
            // 校验资金账户余额
            BigDecimal balance = capitalService.getUserBalance().getData();
            int compare = balance.compareTo(dto.getQuantity());
            if (compare < 0) {
                throw new TadpoleException(AbnormalEnum.MSG.UNKNOWN.getCode(), "Insufficient withdrawable balance");
            }
            // 预扣除资金
            capitalMapper.updateUserBalance(dto.getQuantity().negate(), userId);
            // 记录提现申请
            WithdrawalRecord withdrawalRecordSave = new WithdrawalRecord();
            withdrawalRecordSave.setId(idWorker.nextId());
            withdrawalRecordSave.setQuantity(dto.getQuantity());
            withdrawalRecordSave.setState(WalletStatusEnums.Status.UNDER_REVIEW.getValue());
            withdrawalRecordSave.setUserId(appUser.getId());
            withdrawalRecordSave.setCurrency(WalletConstant.USDT);
            withdrawalRecordSave.setPublicChain(dto.getChain());
            withdrawalRecordSave.setArrivalAddress(dto.getArrivalAddress());
            withdrawalRecordSave.setServiceCharge(currency.getServiceCharge());
            withdrawalRecordMapper.insertWithdrawalRecord(withdrawalRecordSave);
            mysqlUtil.commitLocalTx();
//            stringRedisTemplate.delete(key);
            return R.ok("Cash withdrawal application succeeded!");
        } catch (TadpoleException ignored) {
            mysqlUtil.rollbackLocalTx();
            throw ignored;
        } catch (Exception e) {
            mysqlUtil.rollbackLocalTx();
            throw new TadpoleException(AbnormalEnum.MSG.DISCONNECTED);
        }
    }


    /**
     * 提现审核
     *
     * @param dto dto
     * @return r
     */
    @Override
    public R<String> toExamine(WithdrawalToExamineDTO dto) {
        // 审核拒绝 => 直接退回资金
        if (dto.getState().equals(WalletStatusEnums.Status.REJECTED.getValue())) {
            return refuse(dto.getId(), dto.getRemarks());
        }
        // 审核通过
        if (dto.getState().equals(WalletStatusEnums.Status.WITHDRAWING.getValue())) {
            return adopt(dto);
        }
        throw new TadpoleException(AbnormalEnum.MSG.UNKNOWN_ORDER_COMPOSITION.getCode(), "审核类型错误");
    }

    /**
     * 审核通过
     *
     * @param dto
     * @return
     */
    private R<String> adopt(WithdrawalToExamineDTO dto) {
        try {
            if (dto.getSourceAddress() == null || dto.getSourceAddress().isEmpty()) {
                throw new TadpoleException(AbnormalEnum.MSG.MANDATORY_PARAM_EMPTY_OR_MALFORMED.getCode(), "来源地址不可为空");
            }
            if (dto.getTransactionHash() == null || dto.getTransactionHash().isEmpty()) {
                throw new TadpoleException(AbnormalEnum.MSG.MANDATORY_PARAM_EMPTY_OR_MALFORMED.getCode(), "交易哈希不可为空");
            }
            if (dto.getActualWithdrawalAmount() == null) {
                throw new TadpoleException(AbnormalEnum.MSG.MANDATORY_PARAM_EMPTY_OR_MALFORMED.getCode(), "实际提现数量不可为空");
            }
            WithdrawalRecord record = withdrawalRecordMapper.selectWithdrawalRecordById(dto.getId());
            if (record == null) {
                throw new TadpoleException(AbnormalEnum.MSG.UNKNOWN.getCode(), "查询不到该提现记录");
            }
            withdrawalRecordMapper.updateWithdrawalRecord(new WithdrawalRecord() {{
                setId(record.getId());
                setState(dto.getState());
                setSourceAddress(dto.getSourceAddress());
                setTransactionHash(dto.getTransactionHash());
                setActualWithdrawalAmount(dto.getActualWithdrawalAmount());
                setRemarks(dto.getRemarks());
            }});
            return R.ok("提现进行中");
        } catch (TadpoleException ignored) {
            mysqlUtil.rollbackLocalTx();
            throw ignored;
        } catch (Exception e) {
            mysqlUtil.rollbackLocalTx();
            throw new TadpoleException(AbnormalEnum.MSG.DISCONNECTED);
        }
    }

    /**
     * 审核拒绝
     *
     * @param id      提现记录id
     * @param remarks 备注
     */
    @Override
    public R<String> refuse(Long id, String remarks) {
        try {
            WithdrawalRecord record = withdrawalRecordMapper.selectWithdrawalRecordById(id);
            if (record == null) {
                throw new TadpoleException(AbnormalEnum.MSG.UNKNOWN.getCode(), "查询不到该提现记录");
            }
            mysqlUtil.startLocalTx();
            // 返还预扣除资金
            capitalMapper.updateUserBalance(record.getQuantity(), record.getUserId());
            // 修改提现申请状态为已拒绝
            record.setState(WalletStatusEnums.Status.REJECTED.getValue());
            record.setRemarks(remarks);
            withdrawalRecordMapper.updateWithdrawalRecord(record);
            mysqlUtil.commitLocalTx();
            return R.ok("提现拒绝成功");
        } catch (TadpoleException ignored) {
            mysqlUtil.rollbackLocalTx();
            throw ignored;
        } catch (Exception e) {
            mysqlUtil.rollbackLocalTx();
            throw new TadpoleException(AbnormalEnum.MSG.DISCONNECTED);
        }
    }

    /**
     * 查询提现记录
     *
     * @param id 提现记录主键
     * @return 提现记录
     */
    @Override
    public WithdrawalRecord selectWithdrawalRecordById(Long id) {
        return withdrawalRecordMapper.selectWithdrawalRecordById(id);
    }

    /**
     * 查询提现记录列表
     *
     * @param withdrawalRecord 提现记录
     * @return 提现记录
     */
    @Override
    public List<WithdrawalRecord> selectWithdrawalRecordList(WithdrawalRecord withdrawalRecord) {
        return withdrawalRecordMapper.selectWithdrawalRecordList(withdrawalRecord);
    }

    /**
     * 新增提现记录
     *
     * @param withdrawalRecord 提现记录
     * @return 结果
     */
    @Override
    public int insertWithdrawalRecord(WithdrawalRecord withdrawalRecord) {
        withdrawalRecord.setCreateTime(DateUtils.getNowDate());
        return withdrawalRecordMapper.insertWithdrawalRecord(withdrawalRecord);
    }

    /**
     * 修改提现记录
     *
     * @param withdrawalRecord 提现记录
     * @return 结果
     */
    @Override
    public int updateWithdrawalRecord(WithdrawalRecord withdrawalRecord) {
        withdrawalRecord.setUpdateTime(DateUtils.getNowDate());
        return withdrawalRecordMapper.updateWithdrawalRecord(withdrawalRecord);
    }

    /**
     * 批量删除提现记录
     *
     * @param ids 需要删除的提现记录主键
     * @return 结果
     */
    @Override
    public int deleteWithdrawalRecordByIds(Long[] ids) {
        return withdrawalRecordMapper.deleteWithdrawalRecordByIds(ids);
    }

    /**
     * 删除提现记录信息
     *
     * @param id 提现记录主键
     * @return 结果
     */
    @Override
    public int deleteWithdrawalRecordById(Long id) {
        return withdrawalRecordMapper.deleteWithdrawalRecordById(id);
    }

    /**
     * 通过
     *
     * @param id id
     * @since 2023/4/1 15:44
     */
    @Override
    public void approve(Long id) {
        WithdrawalRecord data = new WithdrawalRecord();
        data.setId(id);
        data.setState(WalletStatusEnums.Status.SUCCESS.getValue());
        if (withdrawalRecordMapper.updateWithdrawalRecord(data) != 1) {
            throw new TadpoleException();
        }
    }

    /**
     * 拒绝
     *
     * @param id id
     * @since 2023/4/1 15:44
     */
    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void reject(Long id) {
        if (withdrawalRecordMapper.reject(id) != 1) {
            throw new TadpoleException();
        }

        WithdrawalRecord withdrawal = withdrawalRecordMapper.selectWithdrawalRecordById(id);

        // 根据提现申请回滚资金账户
        if (capitalMapper.addBalance(withdrawal.getUserId(), withdrawal.getQuantity()) != 1) {
            throw new TadpoleException();
        }
    }
}
